package com.immediatus.engine.shape;

import com.immediatus.engine.Unit;
import com.immediatus.engine.contracts.IShape;
import com.immediatus.engine.input.TouchEvent;
import com.immediatus.graphics.camera.Camera;
import com.immediatus.graphics.util.OpenGLWrapper;
import com.immediatus.graphics.vertex.VertexBuffer;

import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;

public abstract class Shape extends Unit implements IShape{
    public static final int BLENDFUNCTION_SOURCE_DEFAULT = GL10.GL_SRC_ALPHA;
    public static final int BLENDFUNCTION_DESTINATION_DEFAULT = GL10.GL_ONE_MINUS_SRC_ALPHA;

    public static final int BLENDFUNCTION_SOURCE_PREMULTIPLYALPHA_DEFAULT = GL10.GL_ONE;
    public static final int BLENDFUNCTION_DESTINATION_PREMULTIPLYALPHA_DEFAULT = GL10.GL_ONE_MINUS_SRC_ALPHA;

    protected int _SourceBlendFunction = BLENDFUNCTION_SOURCE_DEFAULT;
    protected int _DestinationBlendFunction = BLENDFUNCTION_DESTINATION_DEFAULT;

    private boolean _cullingEnabled = false;

    public Shape(final float x_, final float y_){
        super(x_, y_);
    }

    public boolean isVertexBufferManaged(){
        return this.getVertexBuffer().isManaged();
    }

    public void setVertexBufferManaged(final boolean vertexBufferManaged_){
        this.getVertexBuffer().setManaged(vertexBufferManaged_);
    }

    @Override
    public void setBlendFunction(final int sourceBlendFunction_, final int destinationBlendFunction_){
        this._SourceBlendFunction = sourceBlendFunction_;
        this._DestinationBlendFunction = destinationBlendFunction_;
    }

    @Override
    public boolean isCullingEnabled(){
        return this._cullingEnabled;
    }

    @Override
    public void setCullingEnabled(final boolean cullingEnabled_){
        this._cullingEnabled = cullingEnabled_;
    }

    @Override
    public float getWidthScaled(){
        return this.getWidth() * this.getScaleX();
    }

    @Override
    public float getHeightScaled(){
        return this.getHeight() * this.getScaleY();
    }

    @Override
    protected void doDraw(final GL10 gl_, final Camera camera_){
        this.onInitDraw(gl_);
        this.onApplyVertices(gl_);
        this.drawVertices(gl_, camera_);
    }

    @Override
    public boolean onAreaTouched(final TouchEvent event_, final float x_, final float y_){
        return false;
    }

    protected abstract boolean isCulled(final Camera camera_);

    @Override
    protected void onManagedDraw(final GL10 gl_, final Camera camera_){
        if (!this._cullingEnabled || !this.isCulled(camera_)){
            super.onManagedDraw(gl_, camera_);
        }
    }

    @Override
    public void reset(){
        super.reset();
        this._SourceBlendFunction = BLENDFUNCTION_SOURCE_DEFAULT;
        this._DestinationBlendFunction = BLENDFUNCTION_DESTINATION_DEFAULT;
    }

    @Override
    protected void finalize() throws Throwable{
        super.finalize();

        final VertexBuffer vertexBuffer = this.getVertexBuffer();
        if (vertexBuffer.isManaged()){
            vertexBuffer.unloadFromActiveBufferObjectManager();
        }
    }

    protected abstract void onUpdateVertexBuffer();

    protected abstract VertexBuffer getVertexBuffer();

    protected abstract void drawVertices(final GL10 gl_, final Camera camera_);

    protected void onInitDraw(final GL10 gl_){
        OpenGLWrapper.setColor(gl_, this.getRed(), this.getGreen(), this.getBlue(), this.getAlpha());
        OpenGLWrapper.enableVertexArray(gl_);
        OpenGLWrapper.blendFunction(gl_, this._SourceBlendFunction, this._DestinationBlendFunction);
    }

    protected void onApplyVertices(final GL10 gl_){
        if (OpenGLWrapper.EXTENSIONS_VERTEXBUFFEROBJECTS){
            final GL11 gl11 = (GL11) gl_;
            this.getVertexBuffer().selectOnHardware(gl11);
            OpenGLWrapper.vertexZeroPointer(gl11);
        } else{
            OpenGLWrapper.vertexPointer(gl_, this.getVertexBuffer().getFloatBuffer());
        }
    }

    protected void updateVertexBuffer(){
        this.onUpdateVertexBuffer();
    }
}
